/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <string.h>
#include <stdlib.h>
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "os.h"
#include "xr_wifi_adapter.h"
#include "net/wlan/wlan_ext_req.h"

static xr_wifi_event_cb g_evt_cb = NULL;

static xr_wifi_ap_sta_info g_softap_sta_list[XR_WIFI_MAX_AP_STA_NUM];
static int g_softap_sta_index[XR_WIFI_MAX_AP_STA_NUM];

static void xr_wifi_scan_done_callback()
{
    xr_wifi_event e;
    int res_num = 0;

    if (g_evt_cb == NULL) {
        return;
    }

    if (0 != wlan_sta_get_scan_result_num(&res_num)) {
        return;
    }

    e.info.wifi_scan_done.bss_num = res_num;
    e.event = XR_WIFI_EVT_SCAN_DONE;

    g_evt_cb(&e);
}

static xr_wifi_status g_sta_connected_info;

static void xr_wifi_connected_callback()
{
    xr_wifi_event e;

    if (g_evt_cb == NULL) {
        return;
    }

    if (XR_WIFI_OK != xr_wifi_sta_get_connect_info(&g_sta_connected_info)) {
        return;
    }

    e.info.wifi_connected.ssid_len = strlen(g_sta_connected_info.ssid) + 1;
    memcpy(e.info.wifi_connected.ssid, g_sta_connected_info.ssid,
           strlen(g_sta_connected_info.ssid));
    WIFI_MAC_ADDR_COPY(e.info.wifi_connected.bssid,
               g_sta_connected_info.bssid);
    e.event = XR_WIFI_EVT_CONNECTED;

    g_evt_cb(&e);
}

static void xr_wifi_disconnected_callback()
{
    xr_wifi_event e;

    if (g_evt_cb == NULL) {
        return;
    }

    WIFI_MAC_ADDR_COPY(e.info.wifi_disconnected.bssid,
               g_sta_connected_info.bssid);
    e.info.wifi_disconnected.reason_code = -1; /* faked */
    e.event = XR_WIFI_EVT_DISCONNECTED;

    g_sta_connected_info.status = XR_WIFI_DISCONNECTED;

    g_evt_cb(&e);
}

static void xr_wifi_sta_no_network_callback()
{
    xr_wifi_event e;

    if (g_evt_cb == NULL) {
        return;
    }
        
    e.event = XR_WIFI_EVT_STA_FCON_NO_NETWORK;
    g_evt_cb(&e);
}

static int softap_set_sta_join(xr_wifi_ap_sta_info *new_list,
                   uint32_t new_list_num)
{
    int i = 0, j = 0;
    int idle_idx = 0;

    j = 0;
    while (j++ < XR_WIFI_MAX_AP_STA_NUM) {
        if (g_softap_sta_index[j] == 0) {
            idle_idx = j;
            break;
        }
    }

    for (i = 0; i < new_list_num; i++) {
        int finded = 0;
        j = 0;
        while (j < XR_WIFI_MAX_AP_STA_NUM) {
            if (g_softap_sta_index[j] == 0) {
                ++j;
                continue;
            }

            if (WIFI_MAC_ADDR_EQ(new_list[i].mac,
                         g_softap_sta_list[j].mac)) {
                finded = 1;
                break;
            }
            ++j;
        }

        if (finded == 0) {
            g_softap_sta_index[idle_idx] = 1;
            WIFI_MAC_ADDR_COPY(g_softap_sta_list[idle_idx].mac,
                       new_list[i].mac);
            break;
        }
    }

    if (i < new_list_num) {
        return idle_idx;
    } else {
        return -1;
    }
}

static int softap_set_sta_leave(xr_wifi_ap_sta_info *new_list,
                uint32_t new_list_num)
{
    int i = 0, j = 0;

    while (j < XR_WIFI_MAX_AP_STA_NUM) {
        int finded = 0;
        if (g_softap_sta_index[j] == 0) {
            ++j;
            continue;
        }

        for (i = 0; i < new_list_num; i++) {
            if (WIFI_MAC_ADDR_EQ(new_list[i].mac,
                         g_softap_sta_list[j].mac)) {
                finded = 1;
                break;
            }
        }

        if (finded == 0) {
            g_softap_sta_index[j] = 0;
            break;
        }
        ++j;
    }

    if (j < XR_WIFI_MAX_AP_STA_NUM) {
        return j;
    } else {
        return -1;
    }
}

static void xr_wifi_ap_sta_connected_callback()
{
    xr_wifi_event e;
    uint32_t sta_list_num;
    uint32_t join_idx;

    if (g_evt_cb == NULL) {
        return;
    }

    sta_list_num = XR_WIFI_MAX_AP_STA_NUM;
    xr_wifi_ap_sta_info *sta_list =
        malloc(sizeof(xr_wifi_ap_sta_info) * sta_list_num);

    if (XR_WIFI_OK !=
        xr_wifi_softap_get_connected_sta(sta_list, &sta_list_num)) {
        return;
    }
    join_idx = softap_set_sta_join(sta_list, sta_list_num);

    WIFI_MAC_ADDR_COPY(e.info.ap_sta_connected.addr,
               g_softap_sta_list[join_idx].mac);
    e.event = XR_WIFI_EVT_STA_CONNECTED;

    g_evt_cb(&e);
    free(sta_list);
}

static void xr_wifi_ap_sta_disconnected_callback()
{
    xr_wifi_event e;
    uint32_t sta_list_num;
    uint32_t leave_idx;

    if (g_evt_cb == NULL) {
        return;
    }
        
    sta_list_num = XR_WIFI_MAX_AP_STA_NUM;
    xr_wifi_ap_sta_info *sta_list =
        malloc(sizeof(xr_wifi_ap_sta_info) * sta_list_num);

    if (XR_WIFI_OK !=
        xr_wifi_softap_get_connected_sta(sta_list, &sta_list_num)) {
        return;
    }
    leave_idx = softap_set_sta_leave(sta_list, sta_list_num);

    WIFI_MAC_ADDR_COPY(e.info.ap_sta_disconnected.addr,
               g_softap_sta_list[leave_idx].mac);
    e.info.ap_sta_disconnected.reason_code = -1; /* faked */
    e.event = XR_WIFI_EVT_STA_DISCONNECTED;

    g_evt_cb(&e);
    free(sta_list);
}

static void xr_wifi_ap_started_callback()
{
    if (g_evt_cb == NULL) {
        return;
    }

    xr_wifi_event e;
    e.event = XR_WIFI_EVT_AP_START;

    g_evt_cb(&e);
}

static void xr_wifi_event_process(uint32_t event, uint32_t data, void *arg)
{
    struct sysinfo *sysinfo = sysinfo_get();

    uint16_t type = EVENT_SUBTYPE(event);

    switch (type) {
    case NET_CTRL_MSG_WLAN_SCAN_SUCCESS:
        xr_wifi_scan_done_callback();
        break;
    case NET_CTRL_MSG_WLAN_SCAN_FAILED:
        break;
    case NET_CTRL_MSG_WLAN_CONNECTED:
        if (sysinfo->wlan_mode == WLAN_MODE_STA) {
            xr_wifi_connected_callback();
        }
        break;
    case NET_CTRL_MSG_WLAN_DISCONNECTED:
        if (sysinfo->wlan_mode == WLAN_MODE_STA) {
            xr_wifi_disconnected_callback();
        }
        break;
    case NET_CTRL_MSG_NETWORK_UP:
        if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP) {
            xr_wifi_ap_started_callback();
        }
        break;
    case NET_CTRL_MSG_WLAN_AP_STA_CONNECTED:
        if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP) {
            xr_wifi_ap_sta_connected_callback();
        }
        break;
    case NET_CTRL_MSG_WLAN_AP_STA_DISCONNECTED:
        if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP) {
            xr_wifi_ap_sta_disconnected_callback();
        }
        break;
    case NET_CTRL_MSG_CONNECTION_LOSS:
    case NET_CTRL_MSG_WLAN_CONNECT_FAILED:
        if (sysinfo->wlan_mode == WLAN_MODE_STA) {
            xr_wifi_sta_no_network_callback();
        }
        break;
    case NET_CTRL_MSG_NETWORK_DOWN:
    case NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED:
    default:
        break;
    }
}

static observer_base *net_ob = NULL;

int xr_wifi_event_oberser_create()
{
    if (net_ob != NULL) {
        return XR_WIFI_OK;
    }

    net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK,
                          NET_CTRL_MSG_ALL,
                          xr_wifi_event_process, NULL);

    if (net_ob == NULL || sys_ctrl_attach(net_ob) != 0) {
        return XR_WIFI_FAIL;
    }

    return XR_WIFI_OK;
}

int xr_wifi_event_oberser_destroy()
{
    if (0 == sys_callback_observer_destroy(net_ob)) {
        return XR_WIFI_OK;
    }
    else {
        return XR_WIFI_FAIL;
    }
}

int xr_wifi_register_event_callback(xr_wifi_event_cb event_cb)
{
    g_evt_cb = event_cb;
    return XR_WIFI_OK;
}

int xr_wifi_config_callback(uint8_t mode, uint8_t task_prio, uint8_t stack_size)
{
    if (mode == 1) {
        return xr_wifi_event_oberser_create();
    } else {
        return xr_wifi_event_oberser_destroy();
    }
}

static void set_ap_param(int auth_alg, int key_mgmt, int wpa_cipher,
             int rsn_cipher, int proto)
{
    wlan_ap_config_t config;

    config.field = WLAN_STA_FIELD_AUTH_ALG;
    config.u.auth_alg = auth_alg;
    wlan_ap_set_config(&config);

    config.field = WLAN_AP_FIELD_KEY_MGMT;
    config.u.key_mgmt = key_mgmt;
    wlan_ap_set_config(&config);

    config.field = WLAN_AP_FIELD_WPA_PAIRWISE_CIPHER;
    config.u.wpa_cipher = wpa_cipher;
    wlan_ap_set_config(&config);

    config.field = WLAN_AP_FIELD_RSN_PAIRWISE_CIPHER;
    config.u.rsn_cipher = rsn_cipher;
    wlan_ap_set_config(&config);

    config.field = WLAN_AP_FIELD_PROTO;
    config.u.proto = proto;
    wlan_ap_set_config(&config);
}

int xr_wifi_softap_start(xr_wifi_softap_config *conf, char *ifname, int *len)
{
    wlan_ap_config_t config;

    if (0 != net_switch_mode(WLAN_MODE_HOSTAP)) {
        return XR_WIFI_FAIL;
    }

    wlan_ap_set((uint8_t *)conf->ssid, strlen(conf->ssid),
            (uint8_t *)conf->key);

    config.field = WLAN_AP_FIELD_CHANNEL;
    config.u.channel = conf->channel_num;
    wlan_ap_set_config(&config);

    config.field = WLAN_AP_FIELD_IEEE80211N;
    config.u.ieee80211n = 1;
    wlan_ap_set_config(&config);

    switch (conf->authmode) {
    case XR_WIFI_SECURITY_OPEN:
        set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_NONE, 0, 0, 0);
        break;

    case XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX:
        set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK,
                 WPA_CIPHER_TKIP, WPA_CIPHER_CCMP,
                 WPA_PROTO_WPA | WPA_PROTO_RSN);
        break;

    case XR_WIFI_SECURITY_WPA2PSK:
        set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK, 0,
                 WPA_CIPHER_CCMP, WPA_PROTO_RSN);
        break;

    case XR_WIFI_SECURITY_WPAPSK:
        set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK,
                 WPA_CIPHER_TKIP, 0, WPA_PROTO_WPA);
        break;

    case XR_WIFI_SECURITY_WEP:
        set_ap_param(WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED,
                 WPA_KEY_MGMT_NONE,
                 WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104,
                 WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104,
                 WPA_PROTO_WPA | WPA_PROTO_RSN);
        break;

    /* TODO: XR_WIFI_SECURITY_SAE (WPA3) */
    case XR_WIFI_SECURITY_SAE:

    /* The following items are not needed for the time being */
    case XR_WIFI_SECURITY_WPA:
    case XR_WIFI_SECURITY_WPA2:
    case XR_WIFI_SECURITY_UNKNOWN:

    default:
        return XR_WIFI_FAIL;
    }

    wlan_ap_disable();

    if (0 != wlan_ap_enable()) {
        return XR_WIFI_FAIL;
    }

    WIFI_SET_AP_IFNAME(ifname, len);

    return XR_WIFI_OK;
}

int xr_wifi_softap_stop(void)
{
    return (wlan_ap_disable() == 0) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_softap_get_connected_sta(xr_wifi_ap_sta_info *sta_list,
                     uint32_t *sta_num)
{
    wlan_ap_stas_t stas;

    stas.sta = (wlan_ap_sta_t *)sta_list;
    stas.size = *sta_num;

    if (wlan_ap_sta_info(&stas) != 0) {
        return XR_WIFI_FAIL;
    }

    *sta_num = stas.num;

    return XR_WIFI_OK;
}

int xr_wifi_get_channel(const char *ifname, uint8_t ifname_len)
{
    if (ifname[0] != 'a' || ifname[1] != 'p') {
        return XR_WIFI_INVALID_CHANNEL;
    }

    wlan_ap_config_t config;
    config.field = WLAN_AP_FIELD_CHANNEL;
    wlan_ap_get_config(&config);

    return config.u.channel;
}

int xr_wifi_sta_start(char *ifname, int *len)
{
    WIFI_SET_STA_IFNAME(ifname, len);
    return (net_switch_mode(WLAN_MODE_STA) == 0) ? XR_WIFI_OK :
                                 XR_WIFI_FAIL;
}

int xr_wifi_sta_set_reconnect_policy(int enable, uint32_t seconds,
                     uint32_t period, uint32_t max_try_count)
{
    wlan_sta_set_autoconnect(enable);
    return XR_WIFI_OK;
}

int xr_wifi_sta_stop(void)
{
    return (wlan_sta_disable() == 0) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

static int xr_wifi_normal_connect(char *ssid, char *passwd)
{
    uint8_t *psk = NULL;
    uint8_t ssid_len;

    if (ssid) {
        ssid_len = strlen(ssid);
    } else {
        return XR_WIFI_FAIL;
    }

    if (ssid_len > WLAN_SSID_MAX_LEN) {
        ssid_len = WLAN_SSID_MAX_LEN;
    }

    if (0 != net_switch_mode(WLAN_MODE_STA)) {
        return XR_WIFI_FAIL;
    }

    if (0 != wlan_sta_disable()) {
        return XR_WIFI_FAIL;
    }

    if (passwd) {
        if (strlen(passwd) == 0) {
            psk = NULL;
        } else {
            psk = (uint8_t *)passwd;
        }
    } else {
        psk = NULL;
    }

    if (0 != wlan_sta_set((uint8_t *)ssid, ssid_len, psk)) {
        return XR_WIFI_FAIL;
    }

    if (0 != wlan_sta_enable()) {
        return XR_WIFI_FAIL;
    }

    return XR_WIFI_OK;
}

static int wep_passwd_check(uint8_t passwd_len)
{
    if (passwd_len == XR_WIFI_WEP40_ASCII_LEN ||
        passwd_len == XR_WIFI_WEP40_HEX_LEN ||
        passwd_len == XR_WIFI_WEP104_ASCII_LEN ||
        passwd_len == XR_WIFI_WEP104_HEX_LEN) {
            return 1;
        } else {
            return 0;
        }
}

static int xr_wifi_wep_connect(char *ssid, char *passwd)
{
    uint8_t ssid_len;
    wlan_sta_config_t config;

    if (ssid) {
        ssid_len = strlen(ssid);
    } else {
        return XR_WIFI_FAIL;
    }

    if (!wep_passwd_check(strlen(passwd))) {
        return XR_WIFI_FAIL;
    }

    if (ssid_len > WLAN_SSID_MAX_LEN) {
        ssid_len = WLAN_SSID_MAX_LEN;
    }

    if (0 != net_switch_mode(WLAN_MODE_STA)) {
        return XR_WIFI_FAIL;
    }

    if (0 != wlan_sta_disable()) {
        return XR_WIFI_FAIL;
    }

    memset(&config, 0, sizeof(config));

    config.field = WLAN_STA_FIELD_SSID;
    memcpy(config.u.ssid.ssid, ssid, ssid_len);
    config.u.ssid.ssid_len = ssid_len;
    if (0 != wlan_sta_set_config(&config)) {
        return XR_WIFI_FAIL;
    }

    config.field = WLAN_STA_FIELD_WEP_KEY0;
    strlcpy((char *)config.u.wep_key, passwd, sizeof(config.u.wep_key));
    if (0 != wlan_sta_set_config(&config)) {
        return XR_WIFI_FAIL;
    }

    config.field = WLAN_STA_FIELD_WEP_KEY_INDEX;
    config.u.wep_tx_keyidx = 0;
    if (0 != wlan_sta_set_config(&config)) {
        return XR_WIFI_FAIL;
    }

    config.field = WLAN_STA_FIELD_AUTH_ALG;
    config.u.auth_alg = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
    if (0 != wlan_sta_set_config(&config)) {
        return XR_WIFI_FAIL;
    }

    config.field = WLAN_STA_FIELD_KEY_MGMT;
    config.u.key_mgmt = WPA_KEY_MGMT_NONE;
    if (0 != wlan_sta_set_config(&config)) {
        return XR_WIFI_FAIL;
    }

    if (0 != wlan_sta_enable()) {
        return XR_WIFI_FAIL;
    }

    return XR_WIFI_OK;
}

int xr_wifi_sta_connect(xr_wifi_assoc_request *req)
{
    int ret = XR_WIFI_FAIL;

    if (req == NULL) {
        return XR_WIFI_FAIL;
    }

    switch (req->auth) {
    case XR_WIFI_SECURITY_OPEN:
        *(req->key) = NULL;
    case XR_WIFI_SECURITY_WPAPSK:
    case XR_WIFI_SECURITY_WPA2PSK:
    case XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX:
    case XR_WIFI_SECURITY_WPA:
    case XR_WIFI_SECURITY_WPA2:
    case XR_WIFI_SECURITY_SAE:
        ret = xr_wifi_normal_connect(req->ssid, req->key);
        break;
    case XR_WIFI_SECURITY_WEP:
        ret = xr_wifi_wep_connect(req->ssid, req->key);
        break;
    default:
        ret = XR_WIFI_FAIL;
        break;
    }

    return ret;
}

int xr_wifi_sta_fast_connect(xr_wifi_fast_assoc_request *fast_request)
{
    return xr_wifi_sta_connect(&fast_request->req);
}

int xr_wifi_sta_get_connect_info(xr_wifi_status *connect_status)
{
    wlan_sta_ap_t ap_info;
    wlan_sta_states_t state;

    wlan_sta_state(&state);
    switch (state) {
    case WLAN_STA_STATE_DISCONNECTED:
        connect_status->status = XR_WIFI_DISCONNECTED;
        break;
    case WLAN_STA_STATE_CONNECTED:
        connect_status->status = XR_WIFI_CONNECTED;
        break;
    default:
        break;
    }

    if (connect_status->status != XR_WIFI_DISCONNECTED) {
        wlan_sta_ap_info(&ap_info);
        memcpy(connect_status->ssid, ap_info.ssid.ssid,
               ap_info.ssid.ssid_len);
        WIFI_MAC_ADDR_COPY(connect_status->bssid, ap_info.bssid);
        connect_status->channel = ap_info.channel;
    }

    return XR_WIFI_OK;
}

int xr_wifi_sta_scan(void)
{
    int ret = wlan_sta_bss_max_count(64);
    if (ret < 0) {
        return (ret == 0 || ret == -2) ? XR_WIFI_OK : XR_WIFI_FAIL;
    }

    ret = wlan_sta_scan_once();
    return (ret == 0 || ret == -2) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_sacn_freq(uint8_t channel)
{
    int freq[14];
    wlan_ext_scan_freq_t param = { 0 };

    if ((channel == 0) || (channel >= 14)) {
        return XR_WIFI_FAIL;
    }

    freq[0] = (2704 + 5 * channel);
    param.freq_num = freq;

    return wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_SET_SCAN_FREQ,
                (int)(&param)) == 0 ?
                 XR_WIFI_OK :
                 XR_WIFI_FAIL;
}

int xr_wifi_sta_advance_scan(xr_wifi_scan_params *sp)
{
    int ret = -1, TimeOutCNT = 0;
    wlan_sta_config_t sta_config;
    wlan_sta_scan_param_t param;
    unsigned char errorbssid[6] = { 0, 0, 0, 0, 0, 0 };
    switch (sp->scan_type) {
    case XR_WIFI_BASIC_SCAN:
        ret = wlan_sta_scan_once();
        while (ret == -2) { //time out
            OS_MSleep(100);
            ret = wlan_sta_scan_once();
            if ((++TimeOutCNT) >= 20) {
                break;
            }
        }
        break;

    case XR_WIFI_SSID_SCAN:
        if (sp->ssid_len != 0) {
            sta_config.field = WLAN_STA_FIELD_SSID;
            sta_config.u.ssid.ssid_len =
                strlen((const char *)sp->ssid);
            memcpy(sta_config.u.ssid.ssid, sp->ssid,
                   WLAN_SSID_MAX_LEN);
            wlan_sta_set_config(&sta_config);

            sta_config.field = WLAN_STA_FIELD_SCAN_SSID;
            sta_config.u.scan_ssid = 1;
            wlan_sta_set_config(&sta_config);

            param.scan_only = 0;
            param.scan_passive = 0;
            param.scan_ssid = 1;

            ret = wlan_sta_scan(&param);
        }
        break;

    case XR_WIFI_CHANNEL_SCAN:
        if (xr_wifi_sacn_freq(sp->channel) == XR_WIFI_OK) {
            ret = wlan_sta_scan_once();
        }
        break;
    //case XR_WIFI_SSID_PREFIX_SCAN:
    case XR_WIFI_BSSID_SCAN:
        if (memcmp(sp->bssid, errorbssid, 6) != 0) {
            ret = wlan_sta_scan_once();
            while (ret == -2) { //time out
                OS_MSleep(100);
                ret = wlan_sta_scan_once();
                if ((++TimeOutCNT) >= 20) {
                    break;
                }
            }
        }
        break;
    default:
        ret = -1;
        break;
    }

    return ret == 0 ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_sta_disconnect(void)
{
    if (0 != wlan_sta_disable()) {
        return XR_WIFI_FAIL;
    }

    return XR_WIFI_OK;
}

static xr_wifi_auth_mode parse_auth_mode(wlan_sta_ap_t *ap)
{
    if (ap->wpa_flags & WPA_FLAGS_ESS) {
        if (ap->wpa_flags & WPA_FLAGS_WEP)
            return XR_WIFI_SECURITY_WEP;

        if (ap->wpa_flags & WPA_FLAGS_WPA &&
            ap->wpa_flags & WPA_FLAGS_WPA2)
            if (ap->wpa_key_mgmt & WPA_KEY_MGMT_PSK &&
                ap->wpa2_key_mgmt & WPA_KEY_MGMT_PSK)
                return XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX;

        if (ap->wpa_flags & WPA_FLAGS_WPA) {
            if (ap->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
                return XR_WIFI_SECURITY_WPAPSK;
            else
                return XR_WIFI_SECURITY_WPA;
        }

        if (ap->wpa_flags & WPA_FLAGS_WPA2) {
            if (ap->wpa2_key_mgmt & WPA_KEY_MGMT_PSK)
                return XR_WIFI_SECURITY_WPA2PSK;
            else
                return XR_WIFI_SECURITY_WPA2;
        }

        return XR_WIFI_SECURITY_OPEN;
    }

    /*
    if (ap->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
        ap->wpa2_key_mgmt == WPA_KEY_MGMT_NONE)
        return XR_WIFI_SECURITY_OPEN;
    */

    /* TODO: XR_WIFI_SECURITY_SAE (WPA3) */

    return XR_WIFI_SECURITY_UNKNOWN;
}

int xr_wifi_sta_scan_results(xr_wifi_ap_info *ap_list, uint8_t *ap_num)
{
    int ret = XR_WIFI_FAIL;
    wlan_sta_scan_results_t results;

    results.size = *ap_num;
    results.ap = malloc(results.size * sizeof(wlan_sta_ap_t));
    if (results.ap == NULL) {
        return XR_WIFI_FAIL;
    }

    memset(ap_list, 0, sizeof(xr_wifi_ap_info) * (*ap_num));

    if (wlan_sta_scan_result(&results) == 0) {
        for (int i = 0; i < results.num; i++) {
            memcpy(ap_list[i].ssid, results.ap[i].ssid.ssid,
                   results.ap[i].ssid.ssid_len);
            WIFI_MAC_ADDR_COPY(ap_list[i].bssid,
                       results.ap[i].bssid);
            ap_list[i].channel = results.ap[i].channel;
            ap_list[i].rssi = results.ap[i].rssi;
            ap_list[i].auth = parse_auth_mode(&results.ap[i]);
        }
        ret = XR_WIFI_OK;
    } else {
        ret = XR_WIFI_FAIL;
    }

    *ap_num = results.num;
    free(results.ap);
    return ret;
}

int xr_wifi_sta_get_ap_rssi(void)
{
    wlan_sta_ap_t ap_info;
    wlan_sta_ap_info(&ap_info);
    return ap_info.rssi;
}

int xr_wifi_get_macaddr(char *mac_addr, uint8_t mac_len)
{
    struct sysinfo *sysinfo;

    if (mac_len != WIFI_MAC_ADDR_LEN) {
        return XR_WIFI_FAIL;
    }
        
    sysinfo = sysinfo_get();
    WIFI_MAC_ADDR_COPY(mac_addr, sysinfo->mac_addr);

    return XR_WIFI_OK;
}

int xr_wifi_get_local_ip(const char *netif_name, int *const ip)
{
    struct netif *netif_node = netif_find(netif_name);
    if (netif_node == NULL) {
        XR_WIFI_ERR("netif find fail\r\n");
        return XR_WIFI_FAIL;
    }

#ifdef __CONFIG_LWIP_V1
    ip_addr_t ipAddr;
    ip_addr_t netMask;
    ip_addr_t gateWay;
#elif LWIP_IPV4
    ip4_addr_t ipAddr;
    ip4_addr_t netMask;
    ip4_addr_t gateWay;
#endif

    int ret = netifapi_netif_get_addr(netif_node, &ipAddr, &netMask, &gateWay);
    if (ret == 0) {
        *ip = ip4_addr_get_u32(&ipAddr);
        return XR_WIFI_OK;
    }
    return XR_WIFI_FAIL;
}

void xr_wifi_set_local_ip(const char *netif_name, int gw, int ipaddr,
              int netmask)
{
#ifdef __CONFIG_LWIP_V1
    ip_addr_t st_gw;
    ip_addr_t st_ipaddr;
    ip_addr_t st_netmask;
#elif LWIP_IPV4
    ip4_addr_t st_gw;
    ip4_addr_t st_ipaddr;
    ip4_addr_t st_netmask;
#endif

    struct netif *netif_node = netif_find(netif_name);
    if (netif_node == NULL) {
        XR_WIFI_ERR("netif find fail\r\n");
        return XR_WIFI_FAIL;
    }

    ip4_addr_set_u32(&st_gw, gw);
    ip4_addr_set_u32(&st_ipaddr, ipaddr);
    ip4_addr_set_u32(&st_netmask, netmask);
    netifapi_netif_set_addr(netif_node, &st_ipaddr, &st_netmask, &st_gw);
    return;
}

void xr_wifi_set_dns_server(int pos, int dns_server)
{
#ifdef __CONFIG_LWIP_V1
    ip_addr_t dns_srv_addr;
#elif LWIP_IPV4
    ip4_addr_t dns_srv_addr;
#endif

    ip4_addr_set_u32(&dns_srv_addr, dns_server);
    /* TODO: lwip_dns_setserver(pos, &dns_srv_addr); */
}

void xr_wifi_dhcp_enable(const char *netif_name, int enable)
{
    struct netif *netif_node = netif_find(netif_name);
    if (netif_node == NULL) {
        XR_WIFI_ERR("netif find fail\r\n");
        return XR_WIFI_FAIL;
    }

    if (enable)
        netifapi_dhcp_start(netif_node);
    else
        netifapi_dhcp_stop(netif_node);
}

void xr_wifi_netif_status_set(const char *netif_name, int up)
{
    struct netif *netif_node = netif_find(netif_name);
    if (netif_node == NULL) {
        XR_WIFI_ERR("netif find fail\r\n");
        return XR_WIFI_FAIL;
    }

    if (up) {
        netifapi_netif_set_link_up(netif_node);
        netifapi_netif_set_up(netif_node);
    } else {
        netifapi_netif_set_link_down(netif_node);
        netifapi_netif_set_down(netif_node);
    }
}

int xr_wifi_set_appie(uint8_t type, uint8_t *ie, uint16_t ie_len)
{
    struct netif *nif = g_wlan_netif;

    int ret = wlan_set_appie(nif, type, ie, ie_len);
    if (ret != 0)
        return XR_WIFI_FAIL;

    return XR_WIFI_OK;
}